#include "xdg_shell.h"
#include "util.h"
#include "client.h"
#include <stdlib.h>
#include "config.h"

static void
commitnotify(struct wl_listener *listener, void *data)
{
    Client *c = wl_container_of(listener, c, commit);

    /* mark a pending resize as completed */
    if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
        c->resize = 0;
    else if (c->resize)
        c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
}

static void
mapnotify(struct wl_listener *listener, void *data)
{
    /* Called when the surface is mapped, or ready to display on-screen. */
    Client *c = wl_container_of(listener, c, map);
    int i;

    /* Create scene tree for this client and its border */
    c->scene = &wlr_scene_tree_create(c->server->layers[LyrTile])->node;
    c->scene_surface = c->type == XDGShell
            ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
            : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
    if (client_surface(c))
        client_surface(c)->data = c->scene;
    c->scene->data = c->scene_surface->data = c;

    if (client_is_unmanaged(c)) {
        client_get_geometry(c, &c->geom);
        /* Floating */
        wlr_scene_node_reparent(c->scene, c->server->layers[LyrFloat]);
        wlr_scene_node_set_position(c->scene, c->geom.x + borderpx,
            c->geom.y + borderpx);
        return;
    }

    for (i = 0; i < 4; i++) {
        c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor);
        c->border[i]->node.data = c;
        wlr_scene_rect_set_color(c->border[i], bordercolor);
    }

    /* Initialize client geometry with room for border */
    client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
    client_get_geometry(c, &c->geom);
    c->geom.width += 2 * c->bw;
    c->geom.height += 2 * c->bw;

    /* Insert this client into client lists. */
    wl_list_insert(&clients, &c->link);
    wl_list_insert(&fstack, &c->flink);

    /* Set initial monitor, tags, floating status, and focus */
    applyrules(c);
//    printstatus();

    if (c->isfullscreen)
        setfullscreen(c, 1);

    c->mon->un_map = 1;
}

static void
unmapnotify(struct wl_listener *listener, void *data)
{
    /* Called when the surface is unmapped, and should no longer be shown. */
    Client *c = wl_container_of(listener, c, unmap);
    if (c == c->server->grabc) {
        c->server->cursor_mode = CurNormal;
        c->server->grabc = NULL;
    }

    if (c->mon)
        c->mon->un_map = 1;

    if (client_is_unmanaged(c)) {
        wlr_scene_node_destroy(c->scene);
        return;
    }

    wl_list_remove(&c->link);
    setmon(c, NULL, 0); //bybobbi
    wl_list_remove(&c->flink);
    wlr_scene_node_destroy(c->scene);
//    printstatus();
}

static void
createnotify(struct wl_listener *listener, void *data)
{
    Xdg_shell *xdgsl = wl_container_of(listener, xdgsl, new_xdg_surface);
    /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
     * client, either a toplevel (application window) or popup,
     * or when wlr_layer_shell receives a new popup from a layer.
     * If you want to do something tricky with popups you should check if
     * its parent is wlr_xdg_shell or wlr_layer_shell */
    struct wlr_xdg_surface *xdg_surface = data;
    Client *c;

    if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
        struct wlr_box box;
        xdg_surface->surface->data = wlr_scene_xdg_surface_create(
                xdg_surface->popup->parent->data, xdg_surface);
        if (!(c = client_from_popup(xdg_surface->popup)) || !c->mon)
            return;
        box = c->mon->m;
        box.x -= c->geom.x;
        box.y -= c->geom.y;
        wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box);
        return;
    } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE)
        return;

    /* Allocate a Client for this surface */
    c = xdg_surface->data = ecalloc(1, sizeof(*c));
    c->surface.xdg = xdg_surface;
    c->bw = borderpx;
    c->server = xdgsl->server;

    LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify);
    LISTEN(&xdg_surface->events.map, &c->map, mapnotify);
    LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify);
#if 0
    LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify);
    LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle);
    LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen,
            fullscreennotify);
#endif
    c->isfullscreen = 1;

    wl_list_init(&c->input_popups);
}

Xdg_shell *xdg_shell_create(Server* server)
{
    Xdg_shell* _ = ecalloc(1, sizeof(*_));
    _->server = server;
    _->xdg_shell = wlr_xdg_shell_create(server->dpy);
    wl_signal_add(&_->xdg_shell->events.new_surface, &_->new_xdg_surface);
    _->new_xdg_surface.notify = createnotify;

    return _;
}

void xdg_shell_destroy(Xdg_shell *xdgsl)
{
    free(xdgsl);
}
